use crate::translate;

const FIBONACCI: &str = r#"
(module
  (func $fib (param $n i32) (result i32)
    (if (result i32)
      (i32.eq
        (i32.const 0)
        (get_local $n)
      )
      (then
        (i32.const 1)
      )
      (else
        (if (result i32)
          (i32.eq
            (i32.const 1)
            (get_local $n)
          )
          (then
            (i32.const 1)
          )
          (else
            (i32.add
              ;; fib(n - 1)
              (call $fib
                (i32.add
                  (get_local $n)
                  (i32.const -1)
                )
              )
              ;; fib(n - 2)
              (call $fib
                (i32.add
                  (get_local $n)
                  (i32.const -2)
                )
              )
            )
          )
        )
      )
    )
  )
)
    "#;

// Generated by Rust for the `fib` function in `bench_fibonacci_baseline`
const FIBONACCI_OPT: &str = r"
(module
  (func $fib (param $p0 i32) (result i32)
    (local $l1 i32)
    (set_local $l1
      (i32.const 1))
    (block $B0
      (br_if $B0
        (i32.lt_u
          (get_local $p0)
          (i32.const 2)))
      (set_local $l1
        (i32.const 1))
      (loop $L1
        (set_local $l1
          (i32.add
            (call $fib
              (i32.add
                (get_local $p0)
                (i32.const -1)))
            (get_local $l1)))
        (br_if $L1
          (i32.gt_u
            (tee_local $p0
              (i32.add
                (get_local $p0)
                (i32.const -2)))
            (i32.const 1)))))
    (get_local $l1)))";

#[bench]
fn bench_fibonacci_compile(b: &mut test::Bencher) {
    let wasm = wat::parse_str(FIBONACCI).unwrap();

    b.iter(|| test::black_box(translate(&wasm).unwrap()));
}

#[bench]
fn bench_fibonacci_run(b: &mut test::Bencher) {
    let wasm = wat::parse_str(FIBONACCI_OPT).unwrap();
    let module = translate(&wasm).unwrap();

    b.iter(|| module.execute_func::<_, u32>(0, (20,)));
}

#[bench]
fn bench_fibonacci_compile_run(b: &mut test::Bencher) {
    let wasm = wat::parse_str(FIBONACCI).unwrap();

    b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,)));
}

#[bench]
fn bench_fibonacci_baseline(b: &mut test::Bencher) {
    fn fib(n: i32) -> i32 {
        if n == 0 || n == 1 {
            1
        } else {
            fib(n - 1) + fib(n - 2)
        }
    }

    b.iter(|| test::black_box(fib(test::black_box(20))));
}
